﻿/* 
 * Copyright (C) 2014 Mehdi El Gueddari
 * http://mehdi.me
 *
 * This software may be modified and distributed under the terms
 * of the MIT license.  See the LICENSE file for details.
 */

using System;

namespace EntityFramework.DbContextScope
{
    public class AmbientContextSuppressor : IDisposable
    {
        private DbContextScope _savedScope;
        private bool _disposed;

        public AmbientContextSuppressor()
        {
            _savedScope = DbContextScope.GetAmbientScope();

            // We're hiding the ambient scope but not removing its instance
            // altogether. This is to be tolerant to some programming errors. 
            // 
            // Suppose we removed the ambient scope instance here. If someone
            // was to start a parallel task without suppressing
            // the ambient context and then tried to suppress the ambient
            // context within the parallel task while the original flow
            // of execution was still ongoing (a strange thing to do, I know,
            // but I'm sure this is going to happen), we would end up 
            // removing the ambient context instance of the original flow 
            // of execution from within the parallel flow of execution!
            // 
            // As a result, any code in the original flow of execution
            // that would attempt to access the ambient scope would end up 
            // with a null value since we removed the instance.
            //
            // It would be a fairly nasty bug to track down. So don't let
            // that happen. Hiding the ambient scope (i.e. clearing the CallContext
            // in our execution flow but leaving the ambient scope instance untouched)
            // is safe.
            //我们隐藏了环境作用域，但没有删除它的实例
            //总之。这是为了容忍一些编程错误。

            //假设我们在这里删除了环境作用域实例。如果有人
            //开始一个并行的任务
            //环境上下文，然后尝试抑制环境
            //并行任务中的上下文，而原始流
            //处决仍在进行中（我知道这是件奇怪的事，
            //但我相信这会发生），我们最终会
            //删除原始流的环境上下文实例
            //从并行的执行流中执行！
            //
            //因此，原始执行流中的任何代码
            //试图访问环境作用域的结果是
            //从我们移除实例以来，为空值。
            //
            //要找到它是一个相当讨厌的虫子。所以不要让
            //那是真的。隐藏环境作用域（即清除CallContext
            //在我们的执行流程中，但不影响环境作用域实例）
            //是安全的。
            DbContextScope.HideAmbientScope();
        }

        public void Dispose()
        {
            if (_disposed)
                return;

            if (_savedScope != null)
            {
                DbContextScope.SetAmbientScope(_savedScope);
                _savedScope = null;
            }

            _disposed = true;
        }
    }
}
